import java.io.*;

/**
 * This class is used to search a large indexed file.
 * 
 * @author Fredrik, Viktor
 */
public class IndexSearch {
	/**
	 * Main method, called by JVM.
	 */
	public static void main(String[] args) {
		if (args.length < 1) {
			printUsage();
		} else {
			try {
				searchIndex("korpus", args[0]);
				return;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Prints how to use the application.
	 */
	private static void printUsage() {
		System.out.println("Usage: java IndexSearch searchstring");
	}

	/**
	 * Searches a corpus for a specified word.
	 * 
	 * @param inputFile
	 *            the file where the words are.
	 * @param searchString
	 *            the string to search for.
	 * @throws Exception
	 *             what went wrong.
	 */
	private static void searchIndex(String inputFile, String searchString)
			throws Exception {
		// Check for valid searchString.
		searchString = searchString.toLowerCase();
		if (searchString.length() <= 0
				|| !searchString.matches("[a-z\u00e5\u00e4\u00f6]*")) {
			System.err.println("Error in inputword!");
			return;
		}

		// Initialize all file-readers.
		RandomAccessFile korpusIn, indexIn;
		DataInputStream wordIn;
		ObjectInputStream trieIn;
		FastHashMap trie;
		trieIn = new ObjectInputStream(new BufferedInputStream(new FileInputStream("triefile")));
		trie = (FastHashMap) trieIn.readObject();
		indexIn = new RandomAccessFile("indexfile", "r");
		korpusIn = new RandomAccessFile(inputFile, "r");
		wordIn = new DataInputStream(new BufferedInputStream(new FileInputStream("wordfile")));

		// Prepare the key.
		String key = searchString.substring(0, ((searchString.length() >= 3) ? 3
				: searchString.length()));

		// Get the offset to the word in the word-file form the key.
		int wordOffset = 0;
		try {
			wordOffset = trie.get(key);
		} catch (Exception e) {
			System.err.println("Word not found!");
			return;
		}

		// Go to the offset in the wordfile.
		wordIn.skipBytes(wordOffset);

		// Find the right word.
		boolean done = false;
		while (!done) {
			try {
				String in = wordIn.readUTF();
				String tempKey = in.substring(0, ((in.length() >= 3) ? 3
						: in.length()));
				if (tempKey.compareTo(key) != 0)
					throw new Exception("Key mismatch!");
				done = (in.compareTo(searchString) == 0);
				if (!done)
					wordIn.skipBytes(4);
			} catch (Exception e) {
				System.err.println("Word not found!");
				return;
			}
		}
		// Once found, read the offset to the offsets in the offsetfile.
		int indexOffset = wordIn.readInt();

		// Count number of words
		try {
			wordIn.readUTF();
			int nextOffset = wordIn.readInt();
			int wordCount = (int) ((nextOffset - indexOffset) / 4) - 1;
			System.out.println("Instances of the word " + searchString + ": "
					+ wordCount);
		} catch (Exception e) {
		}
		// Go to the offsets in the offsetfile.
		indexIn.seek(indexOffset);

		// Initialize variables
		int count = 0, countTo = 25;
		int korpusOffset = -1;
		// Prepare a buffered printwriter that prints in correct charset.
		PrintStream out = new PrintStream(System.out, true, "UTF-8");
		// Print all instances of word found in corpus.
		while ((korpusOffset = indexIn.readInt()) >= 0) {
			int seekOffset = korpusOffset - 30;
			if (seekOffset < 0)
				seekOffset = 0;
			korpusIn.seek(seekOffset);
			// Prepare array to be read.
			byte[] byteArray = new byte[60 + searchString.length()];
			// Read the array, 30 chars before and after.
			korpusIn.read(byteArray, 0, 60 + searchString.length());
			// Convert the array to String using ISO-8859-1 encoding.
			String outString = new String(byteArray, "ISO-8859-1");

			// Print the word.
			out.println("..." + outString.replace("\n", " ") + "...");
			out.println("");

			// More than countTo printed? Ask user if he wants more.
			if (++count >= countTo) {
				out.println("Printed 25 instances, do you want to print more? Y/n?");
				out.flush();
				char read = (char) System.in.read();
				if (read == 'n') {
					break;
				} else
					countTo = Integer.MAX_VALUE;
			}
		}
		// Print serachinformation.
		out.println("Printed " + count + " instances of the word "
				+ searchString);
		// Flush the buffered writer.
		out.flush();

		// Close all files.
		trieIn.close();
		indexIn.close();
		wordIn.close();
		korpusIn.close();
	}
}
